﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;
using Seagull.Configuration;
using Seagull.DAL;
using Seagull.DAL.Compress;
using Seagull.Events;
using Seagull.Generic;
using Seagull.Model.Alerts;

namespace Seagull.Jobs
{
    /// <summary>
    /// 压缩工作流程：
    ///     1. 查找本地当日以前，超过最后一次备份的数据日期的所有日期目录
    ///     2. 若压缩目录下已有同名目录则删除（可能是未完成的压缩）
    ///     3. 依次压缩每一天的解析后数据，每压缩完一天的数据，将该日的解析后数据删除
    /// </summary>
    [DebuggerDisplay("{ToString()}")]
    public class CompressJob : MaintenanceJob
    {
        internal const string TempFolderPostfix = "_ZIPTMP";

        private readonly PathHelper _sourcePathHelper;
        private readonly string _sourcePath;
        private readonly string _compressFolder;
        private readonly DateTime _startTime;
        private readonly PathHelper _compressPathHelper;

        public CompressJob(string sourcePath, string compressFolder, DateTime startTime)
        {
            _sourcePath = sourcePath;
            _compressFolder = compressFolder;
            _startTime = startTime;
            _sourcePathHelper = new PathHelper(_sourcePath);
            _compressPathHelper = new PathHelper(_compressFolder, Compresser.DefaultCompressedFileExtension);
        }

        public override void Start(CancellationToken cancellationToken)
        {
            FileHelper.CreateDirectory(_compressFolder);

            CleanTempFolders();

            var dirsToCompress = GetDirsToCompress();

            var compressers = dirsToCompress.Select(dateDir => new
            {
                Date = dateDir.Item2,
                TargetDir = dateDir.Item1.FullName.Replace(_sourcePath, _compressFolder),
                Compresser = new Compresser(dateDir.Item1.FullName, Config.Instance.PathConfig.CompressedFileExtension)
            }).ToArray();

            var totalFileCount = compressers.Sum(c => c.Compresser.FileCount);
            var compressedFileCount = 0;

            Action<int> reportProgress = compressed => OnProgressChanged((int)((compressed) * 100.0 / totalFileCount));

            foreach(var compresser in compressers)
            {
                cancellationToken.ThrowIfCancellationRequested();

                // 在开始前报告进度
                reportProgress(compressedFileCount);

                // 准备临时目录
                var tempTargetDir = GenericHelper.AppendFolderName(compresser.TargetDir, TempFolderPostfix);

                if(Directory.Exists(tempTargetDir))
                {
                    FileHelper.DeleteDirectory(tempTargetDir);
                }

                compresser.Compresser.FileCompressed += (sender, e) =>
                    reportProgress(compressedFileCount + e.Data);

                FileHelper.CreateDirectory(tempTargetDir);

                try
                {
                    compresser.Compresser.Compress(tempTargetDir, cancellationToken);
                }
                catch(OperationCanceledException)
                {
                    FileHelper.DeleteDirectory(tempTargetDir);
                    throw;
                }

                FileHelper.MoveDirectory(tempTargetDir, compresser.TargetDir);

                compressedFileCount += compresser.Compresser.FileCount;
            }
        }

        private void CleanTempFolders()
        {
            foreach(var tempDirectory in Directory.GetDirectories(_compressFolder, "*" + TempFolderPostfix, SearchOption.TopDirectoryOnly))
            {
                try
                {
                    FileHelper.DeleteDirectory(tempDirectory);
                }
                catch(Exception exception)
                {
                    LogOnlyAlertEvent.Instance.Publish(new ApplicationAlert(exception, "删除压缩临时目录"));
                }
            }
        }

        private IEnumerable<Tuple<DirectoryInfo, DateTime>> GetDirsToCompress()
        {
            return _sourcePathHelper.DateDirectories.Where(t => NeedCompress(t.Item2));
        }

        public override string ToString()
        {
            return string.Format("SourcePath: {0}", _sourcePath);
        }

        private bool NeedCompress(DateTime date)
        {
            var existingDateDirs = _compressPathHelper.DateDirectories.ToArray();
            return
                // 今天以前
                date < DateTime.Today &&
                // 指定日期之后
                    date >= _startTime &&
                // 且还没有备份
                        existingDateDirs.All(t => t.Item2 != date);
        }
    }
}
